package com.alogic.remote.httpclient.filter;

import com.alogic.remote.httpclient.HttpCientFilter;
import com.alogic.remote.httpclient.HttpClientRequest;
import com.alogic.remote.httpclient.HttpClientResponse;
import com.alogic.xscript.Logiclet;
import com.alogic.xscript.LogicletContext;
import com.alogic.xscript.Script;
import com.alogic.xscript.doc.XsObject;
import com.alogic.xscript.doc.json.JsonObject;
import com.anysoft.util.*;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.w3c.dom.Element;
import java.util.HashMap;

/**
 * Bearer token方式验证
 */
public class HttpBearerAuth extends HttpCientFilter.Abstract{
    protected String token;
    protected long ttl = 5 * 60 * 60 * 1000L;
    protected long lastRefreshTime = 0;
    protected Logiclet onToken = null;

    @Override
    public void configure(Element e, Properties p) {
        Properties props = new XmlElementProperties(e,p);
        configure(props);

        Element onTokenElem = XmlTools.getFirstElementByPath(e, "on-token");
        if (onTokenElem != null){
            onToken = Script.create(onTokenElem, props);
        }
    }

    @Override
    public void configure(Properties p){
        super.configure(p);
        ttl = PropertiesConstants.getLong(p,"ttl",ttl,true);
        token = PropertiesConstants.getString(p,"token",token,true);
    }

    @Override
    public void onRequest(HttpClientRequest request) {
        long now = System.currentTimeMillis();
        if (toRefreshToken(now)){
            refreshToken(now);
        }
        if (StringUtils.isNotEmpty(token)){
            request.setHeader("Authorization", String.format("Bearer %s", escapeCRLF(token)));
        }else{
            LOG.warn("Token is null,ignored");
        }
    }

    protected synchronized void refreshToken(long now) {
        if (onToken != null){
            if (toRefreshToken(now)) {
                LogicletContext logicletContext = new LogicletContext(Settings.get());
                try {
                    logicletContext.SetValue("$ttl", String.valueOf(ttl));
                    logicletContext.SetValue("$token", token);
                    XsObject doc = new JsonObject("root", new HashMap<String, Object>());
                    onToken.execute(doc, doc, logicletContext, null);
                    token = PropertiesConstants.getString(logicletContext, "$token", token);
                    ttl = PropertiesConstants.getLong(logicletContext, "$ttl", ttl);
                    lastRefreshTime = now;
                } catch (Exception ex) {
                    LOG.info("Failed to execute onToken script" + ExceptionUtils.getStackTrace(ex));
                }
            }
        }
    }

    @Override
    public void onResponse(HttpClientResponse response) {
        if (response.getStatusCode() >= 400){
            //当本次调用出错的时候，将token过期
            lastRefreshTime = 0;
        }
    }

    protected boolean toRefreshToken(long now){
        return now - lastRefreshTime > ttl || StringUtils.isEmpty(token);
    }

    protected static String escapeCRLF(String value){
        return value.replaceAll("\r|\n", "");
    }
}
